#include "MainWindow.h"
#include "ui_MainWindow.h"

#include "LeGerber/filereader.h"
#include "LeGerber/processor.h"
#include "LeGerber/command.h"
#include "LeGerber/object.h"
#include "LeGerber/graphicsscene.h"
#include "LeGerber/graphicsitem.h"

#include <QElapsedTimer>
#include <QFileSystemModel>
#include <QFileInfo>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsPathItem>
#include <QScrollBar>
#include <QTextEdit>
#include <QToolBar>
#include <QOpenGLWidget>
#include <QComboBox>

using namespace LeGerber;

/*
 * Dock widgwets:
 * - statement lists
 * - object list
 * -
 * View options:
 * - show outline
 * - show dark, show clean
 * View interactions:
 * - can select item
 */
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    auto toolbar = addToolBar("tools");

    m_fileSystemModel = new QFileSystemModel(this);
    m_fileSystemModel->setFilter(QDir::NoDot | QDir::Files | QDir::Dirs);
    m_fileSystemModel->setRootPath("/home/krys/Projects/le-gerber-viewer/samples"/*QDir::currentPath()*/);
    //m_fileSystemModel->setNameFilters(gerberNameFilters());
    ui->fileSystemListView->setModel(m_fileSystemModel);
    auto rootIndex = m_fileSystemModel->index(m_fileSystemModel->rootPath());
    ui->fileSystemListView->setRootIndex(rootIndex);
    ui->fileSystemListView->selectionModel()->select(rootIndex.child(0, 0), QItemSelectionModel::ClearAndSelect);

    //	connect(ui->fileSystemListView, &QListView::clicked,
    //	        this, &MainWindow::onFileSystemViewClicked);
    connect(ui->fileSystemListView, &QListView::activated,
            this, &MainWindow::onFileSystemViewClicked);

    m_fileSystemModel->setNameFilterDisables(false);
    connect(ui->fileSystemFilterLineEdit, &QLineEdit::textChanged,
            this, &MainWindow::onFileSystemFilterChanged);

    m_scene = new GraphicsScene(this);
    m_scene->setBackgroundBrush(QBrush(QColor("#002b36")));
    m_scene->setForegroundBrush(Qt::NoBrush);

    ui->graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
//    ui->graphicsView->setTransform(QTransform(1,  0, 0,
//                                              0, -1, 0,
//                                              0,  0, 1));
    m_viewportUpdateModeComboBox = new QComboBox();
    m_viewportUpdateModeComboBox->addItem("Full", QGraphicsView::FullViewportUpdate);
    m_viewportUpdateModeComboBox->addItem("Minimal", QGraphicsView::MinimalViewportUpdate);
    m_viewportUpdateModeComboBox->addItem("Smart", QGraphicsView::SmartViewportUpdate);
    m_viewportUpdateModeComboBox->addItem("Bounding rect", QGraphicsView::BoundingRectViewportUpdate);
    m_viewportUpdateModeComboBox->addItem("None", QGraphicsView::NoViewportUpdate);

    m_viewportUpdateModeComboBox->setCurrentIndex(m_viewportUpdateModeComboBox->findData(ui->graphicsView->viewportUpdateMode()));
    connect(m_viewportUpdateModeComboBox, SIGNAL(currentIndexChanged(int)),
            this, SLOT(onViewportUpdateModeComboBoxCurrentIndexChanged(int)));
    toolbar->addWidget(m_viewportUpdateModeComboBox);

    m_showOutlineAction = toolbar->addAction("Show outlline", this,
                                             [this]()
    {
        if (m_showOutlineAction->isChecked())
        {
            m_scene->setDarkPen(QPen(QColor("#073642"), 0));
            m_scene->setClearPen(QPen(QColor("#073642"), 0));
        }
        else
        {
            m_scene->setDarkPen(QPen(Qt::NoPen));
            m_scene->setClearPen(QPen(Qt::NoPen));
        }
    });
    m_showOutlineAction->setCheckable(true);
    m_showOutlineAction->setChecked(false);
    m_scene->setDarkBrush(QBrush(QColor("#2aa198")));
    m_scene->setDarkPen(QPen(Qt::NoPen));
    m_scene->setClearBrush(QBrush(QColor("#002b36")));
    m_scene->setClearPen(QPen(Qt::NoPen));


    m_antialiasingAction = toolbar->addAction("Anti-aliasing", this,
                                              [this]()
    {
        ui->graphicsView->setRenderHint(QPainter::Antialiasing, m_antialiasingAction->isChecked());
    });
    m_antialiasingAction->setCheckable(true);
    m_antialiasingAction->setChecked(false);
    ui->graphicsView->setRenderHint(QPainter::Antialiasing, false);
    ui->graphicsView->setRenderHint(QPainter::HighQualityAntialiasing, false);
    ui->graphicsView->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true);
    ui->graphicsView->setOptimizationFlag(QGraphicsView::DontSavePainterState, true);

    m_openglAction = toolbar->addAction("OpenGL", this,
                                        [this]()
    {
        if (m_openglAction->isChecked())
            ui->graphicsView->setViewport(new QOpenGLWidget());
        else
            ui->graphicsView->setViewport(new QWidget());
    });
    m_openglAction->setCheckable(true);
    m_openglAction->setChecked(false);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::onFileSystemViewClicked(const QModelIndex &index)
{
    QString path = m_fileSystemModel->fileInfo(index).absoluteFilePath();
    if (QFileInfo(path).isDir())
    {
        ui->fileSystemFilterLineEdit->clear();
        QModelIndex rootIndex = m_fileSystemModel->setRootPath(path);
        ui->fileSystemListView->setRootIndex(rootIndex);
        ui->fileSystemListView->selectionModel()->select(rootIndex.child(0, 0), QItemSelectionModel::ClearAndSelect);
    }
    else
    {
        ui->graphicsView->setScene(nullptr);
        ui->messagesTextEdit->clear();
        m_scene->clear();
        ui->messagesTextEdit->append(QString("Processing '%1' ...").arg(path));

        QFile file(path);
        if (!file.open(QFile::ReadOnly))
        {
            ui->messagesTextEdit->append(QString("Error opening file '%1': %2").arg(path).arg(file.errorString()));
            return;
        }
        QByteArray data = file.readAll();

        QElapsedTimer timer;
        timer.start();
        FileReader reader;
        reader.setData(data);
        if (!reader.parse())
        {
            ui->messagesTextEdit->append(QString("Failed to parse '%1': %2").arg(path).arg(reader.errorString()));
            return;
        }
        ui->messagesTextEdit->append(QString("Parsed %2 codes in %1ms, %3 warnings")
                                     .arg(timer.elapsed())
                                     .arg(reader.codeCount())
                                     .arg(reader.warningCount()));
        GerberCodeStatistics stats = reader.codeStatistics();

        timer.restart();
        Processor processor;
        auto commands = reader.takeCommands();
        for (Command *command: commands)
        {
            command->execute(&processor); // FIXME: check for failure
            delete command;
        }
        ui->messagesTextEdit->append(QString("Processed %1 commands in %2ms")
                                     .arg(commands.count())
                                     .arg(timer.elapsed()));

        timer.restart();
        int clearCount = 0;
        auto objects = processor.takeObjects();
        for (auto object: objects)
        {
            if (object->polarity == ClearPolarity)
                clearCount++;

            auto item = new GraphicsItem;
            item->setPath(object->painterPath);
            item->setPolarity(object->polarity);
            m_scene->addItem(item);
            item->setEnabled(true);
            delete object;
        }
        ui->messagesTextEdit->append(QString("Rendered %1 objects (%3 clear) in %2ms")
                                     .arg(objects.count())
                                     .arg(timer.elapsed())
                                     .arg(clearCount));

        //        for (auto key: stats.keys())
        //        {
        //            ui->messagesTextEdit->append(QString("%1 %2").arg(key).arg(stats.value(key)));
        //        }
        if (reader.warningCount() > 0)
            ui->messagesTextEdit->append(reader.warnings().join("\n"));
        ui->messagesTextEdit->verticalScrollBar()->setValue(ui->messagesTextEdit->verticalScrollBar()->minimum());
        ui->graphicsView->setScene(m_scene);
        ui->graphicsView->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
    }
}

void MainWindow::onFileSystemFilterChanged(const QString &text)
{
    m_fileSystemModel->setNameFilters(QStringList() << QString("*%1*").arg(text));
}

void MainWindow::onViewportUpdateModeComboBoxCurrentIndexChanged(int index)
{
    auto mode = m_viewportUpdateModeComboBox->currentData().value<QGraphicsView::ViewportUpdateMode>();
    ui->graphicsView->setViewportUpdateMode(mode);
}

QStringList MainWindow::gerberNameFilters()
{
    QStringList result;
    result << "*.g*" << "*.G*";
    return result;
}
